﻿// Xshell XML Configuration File Reader
// 
// <copyright file="XmlConfigReader.cs" company="TaskyMedia LLC">
//   Copyright © 2010 TaskyMedia LLC
// </copyright>

namespace Xshell
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;

    /// <summary>
    /// Xshell configuration reader using XML as the source.
    /// </summary>
    public class XmlConfigReader : IXsConfigReader
    {
        /// <summary>
        /// Internal buffer for the Xml configuration data.
        /// </summary>
        private MemoryStream _xmlData;

        /// <summary>
        /// Metadata cache of reflected types.
        /// </summary>
        private IMetadataCache _metadataCache;

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlConfigReader"/> class.
        /// </summary>
        /// <param name="xmlFilePath">The XML file path.</param>
        public XmlConfigReader(string xmlFilePath)
        {
            if (!File.Exists(xmlFilePath))
            {
                throw new FileNotFoundException("The Xml configuration file could not be found", xmlFilePath);
            }

            // Read all bytes from the file into our internal representation
            byte[] data = File.ReadAllBytes(xmlFilePath);
            this._xmlData = new MemoryStream(data);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlConfigReader"/> class.
        /// </summary>
        /// <param name="xmlStream">The source XML stream.</param>
        public XmlConfigReader(Stream xmlStream)
        {
            try
            {
                // Read all bytes from the stream into our internal representation
                byte[] data = new byte[xmlStream.Length];
                xmlStream.Read(data, 0, data.Length);
                this._xmlData = new MemoryStream(data);
                data = null;
            }
            catch (Exception ex)
            {
                throw new InvalidDataException("The stream could not be processed.", ex);
            }
        }

        /// <summary>
        /// Reads all task sets from the specified configuration source.
        /// </summary>
        /// <returns>Returns an enumerable collection of <see cref="XsTaskSet"/> instances.</returns>
        public IEnumerable<IXsTaskSet> ReadTaskSets()
        {
            if (this._metadataCache == null)
            {
                throw new InvalidOperationException("The metadata cache object has not been set");
            }

            this._xmlData.Position = 0;

            List<IXsTaskSet> taskSetList = new List<IXsTaskSet>();
            XmlReader xr = XmlReader.Create(this._xmlData);

            // Skip past any unsupported XML sections until we reach the Tasks element
            while (!xr.IsStartElement("TaskSets"))
            {
                if (!xr.Read())
                {
                    break;
                }
            }

            // Verify that we did not reach the end of the file
            if (xr.IsStartElement("TaskSets"))
            {
                if (!xr.IsEmptyElement)
                {
                    xr.ReadStartElement();

                    // Read each TaskSet
                    while (xr.IsStartElement("TaskSet"))
                    {
                        XsTaskSet taskSet = XsTaskSet.CreateFromXml(xr, this._metadataCache);
                        if (taskSet != null)
                        {
                            taskSetList.Add(taskSet);
                        }
                    }

                    xr.ReadEndElement();
                }
            }

            xr.Close();
            return taskSetList;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this._xmlData.Dispose();
        }

        /// <summary>
        /// Sets the metadata cache object.
        /// </summary>
        /// <param name="metadataCache">The metadata cache object.</param>
        public void SetMetadataCache(IMetadataCache metadataCache)
        {
            this._metadataCache = metadataCache;
        }
    }
}
